查看原文
其他

.NET redis 客户端开源组件 FreeRedis (继 CSRedisCore 之后重写)

DotNet 2021-09-23

(给DotNet加星标,提升.Net技能

转自:nicye
cnblogs.com/kellynic/p/13943313.html

一、什么是FreeRedis


FreeRedis 是一款 .NET redis 客户端开源组件,以 MIT 协议开源托管于 github,目前支持 .NET 5、.NETCore 2.1+、.NETFramework 4.0+、Xamarin,有可能已经支持 AOT 编译(目前未测试,但会往这个方向走)。


FreeRedis 会严格按照 FreeSql 的开源方式,做好单元测试,兼容平台,简单易用,有问必答,有求必应的态度,为中国 .NET 开源事业做一点点贡献。


感谢大家的支持,项目还未公开就已经获得 66 星。目前项目仍在起步阶段,欢迎小伙伴参与进来,贡献测试、或代码、或建议都可以。


项目当前的状态:


  • 版本 0.0.8(目前不建议使用在生产环境)


  • 单元测试 268 个


  • 支持 集群、哨兵、主从(已通过测试)


  • 支持 连接池


  • 支持 .NET5/.NETCore 2.1+/.NET4.0+


  • 支持 Redis6.0 所有类型


  • 支持 Redis6.0 RESP3 协议


  • API 仍然与 redis-cli 命令保持一致


  • 采用最宽松的开源协议 MIT https://github.com/2881099/FreeRedis


二、项目由来


说来话长,2016 年之前本人写了一年多 nodejs 服务端应用,使用过 node-redis 组件,真心好用。在此期间有同事不停安利 .NET 可以跨平台了,劝我快回来搞 .NET,开始我是抗拒做螃蟹第一人的,不知道是哪天下午闲着蛋疼去体验了一把 .NET Core 1.0-previewXX(不记得哪个版本了)。试了一把被吸引住了,体验感受和 expressjs 像极了,再也看不见以往 webform/mvc 的缺点。


于是我准备入坑了,入坑第一件事除了 hello world,还需要做相关调研:


  • 性能OK


  • 设计OK


  • 发展OK(暂时的定级)


  • 相关组件OK(HttpClient、Redis、Ado.NET、等等基础组件)


初始调研完成之后,接下下就要抽时间选型框架了,最终从众多框架中选择了合适团队的一款:https://github.com/simplcommerce/SimplCommerce ,在这个项目原有基础之上,结合企业规范要求定制改造,大约两个月时间完成了可生产的状态。(框架不求开始尽善尽美,只求使用中不断打磨,最终走向完美)


理想丰满现实骨干,接下来的故事就是遇到生产故障了,StackExchange.Redis、HttpClient 关于这两个组件的问题,以前讲过现在就不说了(万万没想到这么大的组件使用都能出现问题)。吃螃蟹就会掉坑,掉了坑就要想办法解决,最终与 csredis 组件结缘。


以当时的情形纵观 .NET 所有 redis 客户端组件,只有 csredis 源码最易改造支持 .NETCore(水平有限见谅),csredis 2014 年停止更新,本人于 2016 年将其改造支持 .NETCore 为主,以及增加连接池管理、集群、哨兵、redis2.8 以上的命令,在公司项目生产环境使用一年半载之后开源。


  • CSredis 原源码地址:https://github.com/ctstone/csredis


  • CSRedisCore 源码地址:https://github.com/2881099/csredis


CSRedisCore 开源这么久,nuget 下载量达到 60W,收集需求若干,bug 若干(有解决了的、也有未能重现的),基于我已经对 redis 这块很熟悉,然后 redis 5.0/6.0 又新增了蛮多特性,重新写一款 bug 更少、可维护性更好的想法产生了。


经过几个月的墨迹终于走通可用了,项目最终命名:FreeRedis


感谢 Nuget 转让包的大兄弟。


三、如何使用


1、Single machine redis (单机)


public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13");

//cli.Serialize = obj => JsonConvert.SerializeObject(obj);

//cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type);

cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log

cli.Set("key1", "value1");
cli.MSet("key1", "value1", "key2", "value2");

string value1 = cli.Get("key1");
string[] vals = cli.MGet("key1", "key2");


API 仍然与 redis-cli 命令保持一致,所以如果想了解 FreeRedis 每个方法怎么使用,去百度搜索 “redis 命令”,有很多很多很多资料。don't say so much!!!


支持 Redis6.0 支持的所有数据类型:strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, geo, streams And BloomFilter.



如果需要连接 IPv6,连接串请使用: [fe80::b164:55b3:4b4f:7ce6%15]:6379


2、Master-Slave (读写分离)


public static RedisClient cli = new RedisClient(
"127.0.0.1:6379,password=123,defaultDatabase=13",
"127.0.0.1:6380,password=123,defaultDatabase=13",
"127.0.0.1:6381,password=123,defaultDatabase=13"
);
var value = cli.Get("key1");


这样创建的 cli,所有写入命令都会连接 127.0.0.1:6379 执行,所有读取命令只会随机连接 127.0.0.1:6380 或 127.0.0.1:6381 执行。(内部已经为每个命令做好了读写标记)


3、Redis Sentinel (哨兵高可用)


public static RedisClient cli = new RedisClient(
"mymaster,password=123",
new [] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" },
true //是否读写分离
);


哨兵是一个分布式系统,为 Redis 提供高可用性解决方案,当主机 master 宕机之后,会马上出现一个新的 master 可使用,确保服务的正常运作。


哨兵模式还可以设置读写分离,缓解 master 频繁读取数据的压力,缺点:有可能读到的数据不是最新,因为 redis 从 master 同步到 slave 有延时。


4、Redis Cluster (集群)


假如你有一个 Redis Cluster 集群,其中有三个主节点(7001-7003)、三个从节点(7004-7006),则连接此集群的代码:


public static RedisClient cli = new RedisClient(
new ConnectionStringBuilder[] { "192.168.0.2:7001", "192.168.0.2:7002", "192.168.0.2:7003" }
);


5、Subscribe (订阅)


using (cli.Subscribe("abc", ondata)) //wait .Dispose()
{
Console.ReadKey();
}
void ondata(string channel, string data) =>
Console.WriteLine($"{channel} -> {data}");


6、Scripting (脚本)


var r1 = cli.Eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
new[] { "key1", "key2" }, "first", "second") as object[];
var r2 = cli.Eval("return {1,2,{3,'Hello World!'}}") as object[];
cli.Eval("return redis.call('set',KEYS[1],'bar')",
new[] { Guid.NewGuid().ToString() })


7、Pipeline (管道)


using (var pipe = cli.StartPipe())
{
pipe.IncrBy("key1", 10);
pipe.Set("key2", Null);
pipe.Get("key1");
object[] ret = pipe.EndPipe();
Console.WriteLine(ret[0] + ", " + ret[2]);
}
// or Async Callback
using (var pipe = cli.StartPipe())
{
var tasks = new List<Task>();
long t0 = 0;
task.Add(pipe.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback
pipe.SetAsync("key2", Null);
string t2 = null;
task.Add(pipe.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback
pipe.EndPipe();
Task.WaitAll(tasks.ToArray()); //wait all callback
Console.WriteLine(t0 + ", " + t2);
}


8、Transaction (事务)


using (var tran = cli.Multi())
{
tran.IncrBy(key1", 10);
tran.Set("key2", Null);
tran.Get("key1");
object[] ret = tran.Exec();
Console.WriteLine(ret[0] + ", " + ret[2]);
}
// or Async Callback
using (var tran = cli.Multi())
{
var tasks = new List<Task>();
long t0 = 0;
task.Add(tran.IncrByAsync("key1", 10).ContinueWith(t => t0 = t.Result)); //callback
tran.SetAsync("key2", Null);
string t2 = null;
task.Add(tran.GetAsync("key1").ContinueWith(t => t2 = t.Result)); //callback
tran.Exec();
Task.WaitAll(tasks.ToArray()); //wait all callback
Console.WriteLine(t0 + ", " + t2);
}


9、GetDatabase (切库)


using (var db = cli.GetDatabase(10))
{
db.Set("key1", 10);
var val1 = db.Get("key1");
}


四、结束


目前项目仍在起步阶段,欢迎小伙伴参与进来,贡献测试、或代码、或建议都可以。


FreeRedis 使用最宽松的开源协议 MIT https://github.com/2881099/FreeRedis


如果你有好的 redis 实现想法,欢迎给作者留言讨论,谢谢观看!


- EOF -


推荐阅读  点击标题可跳转
一款功能强大的高性能二进制序列化器Bssom.Net一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试使用 C# 9.0 新语法提升 if 语句美感 


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

点赞和在看就是最大的支持❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存